package com.wt.chat.schedule;

import com.wt.chat.cache.RedisCache;
import com.wt.chat.entity.Dialog;
import com.wt.chat.mapper.DialogMapper;
import com.wt.chat.model.KefuModel;
import com.wt.chat.schedule.processor.DialogAssignProcessor;
import com.wt.chat.schedule.processor.ProcessParameter;
import com.wt.chat.service.KefuGroupService;
import com.wt.chat.service.KefuService;
import com.wt.chat.utils.Keys;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @description
 * @author: wangtao
 * @date:17:18 2019/11/18
 * @email:taow02@jumei.com
 */
@Service
public class DefaultAssignScheduleServiceImpl implements AssignScheduleService {

	private final Logger logger = LoggerFactory.getLogger(getClass());

	@Autowired
	private RedisCache redisCache;

	@Autowired
	private DialogMapper dialogMapper;

	@Autowired
	private KefuGroupService kefuGroupService;

	@Autowired
	private List<DialogAssignProcessor> processors;

	private boolean inited = false;

	@Override
	public List<KefuModel> findCanAssignKefu(Dialog dialog) {
		// redis的list，key为groupId，值为可以处理该分组进线的所有kefu的id(这里是所有，并不只是上线的)
		List<String> handleGroupkefu = kefuGroupService.getHandleKefusByGroupId(dialog.getGroupId());
		// redis kefuId为key，kefuInfo为值的map，kefu上线之后就往这里头写，如果没有上线，该key就是null，已经被过滤了，这里得到的都是在线的，
		// 这个和上面分开，客服上下线和更新客服的会话数不影响上面分组缓存
		List<KefuModel> onlineKefus = redisCache.hgetMulti(Keys.KEFU_ONLINE_HASH_KEY, handleGroupkefu, KefuModel.class);
		onlineKefus = onlineKefus.stream()
				.filter(v -> v.getCurrentDialogCount() < (v.getMaxDialog() == null ? 0 : v.getMaxDialog()))
				.collect(Collectors.toList());
		return onlineKefus;
	}

	@Override
	public void assign() {
		Set<String> groupKeys = redisCache.Keys(Keys.getDialogQueueByGroup("*"));
		ProcessParameter parameter = new ProcessParameter();
		groupKeys.forEach(groupKey -> {
			try {
				Dialog dialog = redisCache.leftPop(groupKey, Dialog.class);
				if (dialog == null) {
					return;
				}
				parameter.clear().setDialog(dialog);

				List<KefuModel> onlineKefus = findCanAssignKefu(dialog);
				if (!onlineKefus.isEmpty()) {
					// 随机取一个客服
					KefuModel kefu = onlineKefus.get(new Random().nextInt(onlineKefus.size()));
					updateDialog(dialog, kefu);
					parameter.setKefu(kefu);
				}
				afterAssign(parameter);
			} catch (Exception e) {
				logger.error("分配客服异常", e);
				parameter.setError(e);
			}
		});
	}

	@Override
	public void afterAssign(ProcessParameter parameter) {
		if (parameter.getKefu() == null) {
			assignFailed(parameter);
			return;
		}
		if (!inited) {
			processors.sort(Comparator.comparing(DialogAssignProcessor::getOrder));
		}
		processors.forEach(processor -> {
			if (processor.canProcess(parameter)) {
				processor.process(parameter);
			}
		});
	}

	private void assignFailed(ProcessParameter parameter) {
		Dialog dialog = parameter.getDialog();
		Integer groupId = dialog.getGroupId();
		String key = Keys.getDialogQueueByGroup(groupId + "");
		String info = "会话dialogId=" + dialog.getId() + "，分组groupId=" + groupId + "分配客服失败。";
		if (parameter.getError() != null) {
			logger.error(info + "分配客服时异常！", parameter.getError());
			// 分配失败之后，重新插入队列尾部，等待下一次分配
			redisCache.rightPush(key, dialog);
			return;
		}
		logger.info(info + "没有可用客服！");
		// 正常分配失败之后，重新插入队列首部，等待下一次分配
		redisCache.leftPush(key, dialog);
	}

	private void updateDialog(Dialog dialog, KefuModel kefu) {
		dialog.setKefuId(kefu.getId());
		dialog.setInLine(true);
		dialog.setAcceptTime(new Date());
		dialog.setStatus(Dialog.DialogStatus.OPEN.getValue());
		dialogMapper.updateByPrimaryKeySelective(dialog);
	}

}
